// File:       piscmd03.c++
// Version:    1.00
// Author:     (c) Miles Sabin, 1997
// Purpose:    float/double extraction command

// Change log:
//  27/03/97   v. 1.00

#include "piscmds.h"

#include <errno.h>
#include <stdlib.h>
#include "istream.h"
#include "streambuf.h"


// Implementation of ExtractFloatingCommand

ExtractFloatingCommand::ExtractFloatingCommand(basic_istream_char& is)
  { execute_template(is, false); }

ExtractFloatingCommand::~ExtractFloatingCommand()
  {}

ios::iostate ExtractFloatingCommand::execute(basic_istream_char& is)
  {
    ios::iostate state = ios::failbit;

    basic_streambuf_char* sb = is.rdbuf();

    char buf[64];
    char* p = buf;
    char* end = buf+sizeof(buf)-1;

    int c = sb->sgetc();

    if(c == '+' || c == '-')
    {
      *p++ = c;
      c = sb->snextc();
    }

    if(c >= '0' && c <= '9')
    {
      state &= ~ios::failbit;

      do
      {
        if(p == end)
          goto overflow;
        *p++ = c;

        c = sb->snextc();
      }
      while(c >= '0' && c <= '9');
    }

    if(c == '.')
    {
      if(p == end)
        goto overflow;
      *p++ = c;

      c = sb->snextc();

      if(c >= '0' && c <= '9')
      {
        state &= ~ios::failbit;

        do
        {
          if(p == end)
            goto overflow;
          *p++ = c;

          c = sb->snextc();
        }
        while(c >= '0' && c <= '9');
      }
    }

    if((state&ios::failbit) == 0 && (c == 'e' || c == 'E'))
    {
      if(p == end)
        goto overflow;
      *p++ = c;

      c = sb->snextc();

      if(c == '+' || c == '-')
      {
        if(p == end)
          goto overflow;
        *p++ = c;

        c = sb->snextc();
      }

      if(c >= '0' && c <= '9')
      {
        do
        {
          if(p == end)
            goto overflow;
          *p++ = c;

          c = sb->snextc();
        }
        while(c >= '0' && c <= '9');
      }
      else
        state |= ios::failbit;
    }

    if(c == basic_istream_char::traits::eof())
      state |= ios::eofbit;

    if((state&ios::failbit) == 0)
    {
      errno = 0;
      *p = 0;
      d_ = ::strtod(buf, 0);
      if(errno == ERANGE)
        state |= ios::failbit;
    }

    return state;

overflow:

    return state|ios::failbit;
  }


// Implementation of basic_istream_char

basic_istream_char& basic_istream_char::operator>>(float& f)
  {
    ExtractFloatingCommand cmd(*this);
    if(!fail())
      f = cmd.as_double();
    return *this;
  }

basic_istream_char& basic_istream_char::operator>>(double& f)
  {
    ExtractFloatingCommand cmd(*this);
    if(!fail())
      f = cmd.as_double();
    return *this;
  }
